﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;

using Microsoft.Surface;
using Microsoft.Surface.Presentation;
using Microsoft.Surface.Presentation.Controls;

using ESRI.ArcGIS.Client;
using ESRI.ArcGIS.Client.Geometry;

namespace ENMAX_ArcGIS
{
    /// <summary>
    /// This class represents the scatterview item which will hold the map.
    /// It includes the frame of the map with the toolbar buttons and the actions of these buttons.
    /// Inside of the frame is the actual map control.
    /// </summary>
    public partial class MapFrame : ScatterViewItem
    {
        private DragDropScatterView parentScatterView;
        private TwoPointScatterView tpScatterView;
        private PanelPictureMap pictureMap;
        private TroubleTicket tTicket;

        private GhostFrame linkedGhostFrame;

        public TroubleTicket RelatedTroubleTicket
        {
            get
            {
                return tTicket;
            }
            set
            {
                tTicket = value;
            }
        }

        # region Constructor

        /// <summary>
        /// This is the constructor of the MapFrame...
        /// </summary>
        /// <param name="mainWindow"> Used to inform the mainWindow when a MapFrame is closed/saved </param>
        /// <param name="thePictureMap"> The information of the picture map </param>
        public MapFrame(DragDropScatterView mainScatterView, PanelPictureMap thePictureMap)
        {
            InitializeComponent();
            this.parentScatterView = mainScatterView;
            this.pictureMap = thePictureMap;
                                    
            // Initialize the map
            InitializeMap();
            LoadMap();            
        }

        public MapFrame(TwoPointScatterView mainView, GhostFrame relatedGhostFrame, MapPoint p1, MapPoint p2)
        {
            InitializeComponent();
            this.tpScatterView = mainView;
            linkedGhostFrame = relatedGhostFrame;
            linkedGhostFrame.LinkedMapFrame = this;

            InitializeMap();
            LoadMap(p1, p2);
        }

        public MapFrame(TwoPointScatterView mainView, GhostFrame relatedGhostFrame, Envelope env, SolidColorBrush color)
        {
            InitializeComponent();
            this.tpScatterView = mainView;
            linkedGhostFrame = relatedGhostFrame;
            linkedGhostFrame.LinkedMapFrame = this;

            glassFrame1.Background = color;
            glassFrame2.Background = color;
            glassFrame3.Background = color;
            glassFrame4.Background = color;

            InitializeMap();
            LoadMap(env);
        }

        public MapFrame(TwoPointScatterView mainView, GhostFrame relatedGhostFrame, MapPoint p1, MapPoint p2, SolidColorBrush color)
        {
            InitializeComponent();
            this.tpScatterView = mainView;
            linkedGhostFrame = relatedGhostFrame;
            linkedGhostFrame.LinkedMapFrame = this;

            glassFrame1.Background = color;
            glassFrame2.Background = color;
            glassFrame3.Background = color;
            glassFrame4.Background = color;

            InitializeMap();
            LoadMap(p1, p2);
        }
        
        # endregion

        # region Members
        /// <summary>
        /// Loads an image - for the buttons.
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        private static Image LoadImageFromPath(string path)
        {
            ImageSourceConverter converter = new ImageSourceConverter();
            Image image = new Image();
            image.Source = (ImageSource)converter.ConvertFromString(path);
            return image;
        }

        /// <summary>
        /// TODO: This button is supposed to save the changes done in the map
        /// Currently it closes the frame and save the position of the frame to
        /// TroubleTicket object ONLY.
        /// The changes done to the map should not only reflect on the database but also on the picture maps of the looping panels
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SaveButton_Click(object sender, RoutedEventArgs e)
        {
            if (tTicket != null)
            {
                tTicket.MapFrameEnvelope = MainMap.Map.Extent;
                tTicket.MapFrameWidth = this.Width;
                tTicket.MapFrameHeight = this.Height;
                tTicket.MapFrameCenter = this.Center;
                tTicket.MapFramePreviouslyOpen = true;
                tTicket.MapFrameCurrentlyOpen = false;
                tTicket.MapFrameOrientation = this.Orientation;
            }

            this.tpScatterView.RemoveMap(this);
        }

        # endregion

        # region ArcGisMap

        /// <summary>
        /// Initialize the map
        /// </summary>
        private void InitializeMap()
        {
            MainMap.MapWidth = MainMap.Width;
            MainMap.MapHeight = MainMap.Height;
            MainMap.ShowLayersList = false;
        }

        /// <summary>
        /// Load the map according to the information stored in the PanelPictureMap
        /// </summary>
        private void LoadMap()
        {
            // Set the initial extent from the pictureMap
            MainMap.setExtentFromLtdLng(pictureMap.Latitude, pictureMap.Longitude);
        }

        private void LoadMap(Envelope env)
        {
            MainMap.setExtentFromEnvelop(env);
        }

        private void LoadMap(MapPoint p1, MapPoint p2)
        {
            Envelope env = new Envelope(p1, p2);
            MainMap.setExtentFromEnvelop(env);
            //MainMap.setExtentFromLtdLng(p1.Y, p1.X);
        }

        /// <summary>
        /// Toggle the map type
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void ToggleMapTypeClicked(object sender, RoutedEventArgs e)
        {
            SurfaceButton button = (SurfaceButton)sender;

            if (MainMap.ShowLayersList)
            {
                MainMap.ShowLayersList = false;
            }
            else
            {
                MainMap.ShowLayersList = true;
            }            
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void RefreshMapClicked(object sender, RoutedEventArgs e)
        {
            MainMap.ReloadMap();
        }

        # endregion
        
        # region Map Event Handlers

        /// <summary>
        /// This handler is called when a Contact is first recognized.
        /// </summary>
        /// <param name="sender">the element raising the ContactDownEvent</param>
        /// <param name="args">information about this ContactDownEvent</param>
        private void MainMap_ContactDown(object sender, ContactEventArgs e)
        {
            if (MainMap.isMapInDrawMode())
            {
                MapDrawController.getInstance().ContactDownHandler(sender, e);
            }
            else if (MainMap.ShowLayersList)
            {
                // Do nothing
            }
            else
            {
                MapPanZoomController.getInstance().ContactDownHandler(sender, e);
                linkedGhostFrame.UpdatePosition(MainMap.Extent);
            }
        }

        /// <summary>
        /// This handler is called when Contact capture is lost.
        /// </summary>
        /// <param name="sender">the element raising the LostContactCapture</param>
        /// <param name="args">information about this LostContactCapture event</param>
        private void MainMap_LostContactCapture(object sender, ContactEventArgs e)
        {
            if (MainMap.isMapInDrawMode())
            {
                MapDrawController.getInstance().LostContactCaptureHandler(sender, e);
                
            }
            else if (MainMap.ShowLayersList)
            {
                // Do nothing
            }
            else
            {
                MapPanZoomController.getInstance().LostContactCaptureHandler(sender, e);
                linkedGhostFrame.UpdatePosition(MainMap.Extent);
            }
        }
                      
        /// <summary>
        /// Pan to the point and zoom according to the tapZoomFactor
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MainMap_ContactTapGesture(object sender, ContactEventArgs e)
        {
            if (MainMap.isMapInDrawMode() || MainMap.ShowLayersList) return;

            MapPanZoomController.getInstance().ContactTapGestureHandler(sender, e);
            linkedGhostFrame.UpdatePosition(MainMap.Extent);
        }

        /// <summary>
        /// Handles contact changed event for the map
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MainMap_PreviewContactChanged(object sender, ContactEventArgs e)
        {
            if (MainMap.isMapInDrawMode())
            {
                MapDrawController.getInstance().PreviewContactChangedHandler(sender, e);
            }
            else if (MainMap.ShowLayersList)
            {
                // Do nothing
            }
            else
            {
                MapPanZoomController.getInstance().PreviewContactChangedHandler(sender, e);
                linkedGhostFrame.UpdatePosition(MainMap.Extent);
            }            
        }
                

        # endregion

        # region scalling

        /// <summary>
        /// Changing the size of the map
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ScatterViewItem_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (e.PreviousSize.Height == 0)
                return;
                        
            // Resize the map grid
            MainMap.Width += e.NewSize.Width - e.PreviousSize.Width;
            MainMap.Height += e.NewSize.Height - e.PreviousSize.Height;
            linkedGhostFrame.UpdatePosition(MainMap.Extent);
        }

        # endregion scalling

        # region Map_Graphics

        private void DrawPointButton_Click(object sender, RoutedEventArgs e)
        {
            MainMap.SetDrawPointMode();
        }

        private void DrawPolylineButton_Click(object sender, RoutedEventArgs e)
        {
            MainMap.SetDrawPolylineMode();
        }

        private void DrawPolygonButton_Click(object sender, RoutedEventArgs e)
        {
            MainMap.SetDrawPolygonMode();
        }

        private void DrawRectangleButton_Click(object sender, RoutedEventArgs e)
        {
            MainMap.SetDrawRectangleMode();
        }

        private void DrawFreehandButton_Click(object sender, RoutedEventArgs e)
        {
            MainMap.SetDrawFreehandMode();
        }

        private void ClearGraphicsButton_Click(object sender, RoutedEventArgs e)
        {
            // Annotations Demo Code
            DrawingPadCanvas.Strokes.Clear();

            //MainMap.ClearGraphics();
        }

        private void StopDrawButton_Click(object sender, RoutedEventArgs e)
        {
            MainMap.StopDrawing();
        }

        # endregion Map_Graphics
        
        
        #region Annotations Demo Code

        void InkCanvasOnOffChanged(object sender, RoutedEventArgs e)
        {
            SurfaceButton button = (SurfaceButton)sender;
            if (DrawingPadCanvas.IsHitTestVisible)
            {
                // Prevent the SurfaceinkCanvas from processing contacts
                DrawingPadCanvas.IsHitTestVisible = false;

                // Load the new button image                
                button.Content = LoadImageFromPath("Resources\\Buttons\\ESRI_Stuff\\DrawFreehand.png");
                button.ToolTip = "Draw Freehand";
            }
            else
            {
                // Let the SurfaceinkCanvas start processing contacts
                DrawingPadCanvas.IsHitTestVisible = true;

                // Load the new button image
                button.Content = LoadImageFromPath("Resources\\Buttons\\ESRI_Stuff\\StopDraw.png");
                button.ToolTip = "Stop Drawing";
            }
        }

        private void OnCurrentColorTap(object sender, ContactEventArgs args)
        {
            if (ColorWheel.Visibility == Visibility.Visible)
            {
                ColorWheel.Visibility = Visibility.Hidden;
            }
            else
            {
                ColorWheel.Visibility = Visibility.Visible;
            }
        }

        //==========================================================//
        /// <summary>
        /// Handles the tap event for the color wheel.
        /// </summary>
        /// <param name="sender">The color wheel.</param>
        /// <param name="args">The arguments for the event.</param>
        private void OnColorWheelContactTap(object sender, ContactEventArgs args)
        {
            // Set the color on the CurrentColor indicator and on the SurfaceInkCanvas
            Color color = GetPixelColor(args.Contact);

            // Black means the user touched the transparent part of the wheel. In that 
            // case, leave the color set to its current value
            //if (color != Colors.Black)
            //{
                DrawingPadCanvas.DefaultDrawingAttributes.Color = color;
                CurrentColor.Fill = new SolidColorBrush(color);
            //}

            // Set editing mode to ink
            DrawingPadCanvas.EditingMode = SurfaceInkEditingMode.Ink;

            // Update the button image
            DrawFreehandButton.Content = LoadImageFromPath("Resources\\Buttons\\ESRI_Stuff\\DrawFreehand.png");

            // Replace the color wheel with the current color button
            ColorWheel.Visibility = Visibility.Hidden;
        }

        private void OnColorSelectionPreviewContactDown(object sender, ContactEventArgs args)
        {
            // Capture the contact and handle the event 
            IInputElement element = sender as IInputElement;
            if (element != null && Contacts.CaptureContact(args.Contact, element))
            {
                args.Handled = true;
            }
        }

        private System.Windows.Media.Color GetPixelColor(Contact contact)
        {
            // Translate the point according to whatever transforms are on the color wheel.
            System.Windows.Point rawPoint = contact.GetPosition(ColorWheel);
            System.Windows.Point transformedPoint = ColorWheel.RenderTransform.Transform(rawPoint);

            // The point is outside the color wheel. Return black.
            if (transformedPoint.X < 0 || transformedPoint.X >= ColorWheel.Source.Width ||
                transformedPoint.Y < 0 || transformedPoint.Y >= ColorWheel.Source.Height)
            {
                return Colors.Black;
            }

            // The point is inside the color wheel. Find the color at the point.
            CroppedBitmap cb = new CroppedBitmap(ColorWheel.Source as BitmapSource, new Int32Rect((int)transformedPoint.X, (int)transformedPoint.Y, 1, 1));
            byte[] pixels = new byte[4];
            cb.CopyPixels(pixels, 4, 0);
            return Color.FromRgb(pixels[2], pixels[1], pixels[0]);
        }
        #endregion

        public void SetMapFrameName(string name)
        {
            MapFrameTitle.Text = name;
        }

        public GhostFrame GetGhostFrame()
        {
            return linkedGhostFrame;
        }

        public void UpdateMap(Envelope mapEnv)
        {
            MainMap.setExtentFromEnvelop(mapEnv);
        }

        #region Delegations for Map Layer Changes

        public void MapLayerAdded(Layer addLayer)
        {
            Layer clone = MapLayersManager.Instance.CloneLayer(addLayer);

            if (clone != null)
                MainMap.AddMapLayer(clone);
        }

        public void MapLayerDeleted(Layer deleteLayer)
        {
            MainMap.RemoveMapLayer(deleteLayer);
        }

        #endregion
    }
}
